﻿
using System;
using System.Collections.Generic;
using System.Threading;
using agsXMPP;
using sharpxbot.Core.Connection;
using sharpxbot.Core.Threading;
using sharpxbot.Utils.Logging;
namespace sharpxbot.Core
{
    /// <summary>
    /// Keeps IConnection alive
    /// </summary>
    public class ConnectionManager : IConnectionManager, IActive 
    {
        public const int WaitActivityTimeout = 15000;

        public ConnectionManager(IConnection connection)
        {
            if (connection == null)
                throw new NullReferenceException("Null-reference connection passed to ctor");

            this.connection = connection;
            this.Sessions = new List<ISession>();
        }

        #region IConnectionManager Members

        public IList<ISession> Sessions { get; private set; }

        public void SuspendConnection(TimeSpan delay)
        {
            suspendConnectionTime = delay;
            suspendConnectionRequired = true;
        }

        #endregion

        #region IActive Members

        public bool IsRunning { get; private set; }

        public void Start()
        {
            if (!IsRunning)
            {
                thread = new Thread(new ThreadStart(Run));
                thread.Start();
            }
        }

        public void Stop()
        {
            if (IsRunning)
            {
                terminate = true;
            }
        }

        #endregion

        #region Private methods

        private void Run()
        {
            IsRunning = true;

            while (!terminate)
            {
                SuspendConnectionIfNeeded();

                XmppConnectionState state = GetConnectionState();

                switch (state)
                {
                    case XmppConnectionState.Disconnected:
                        {
                            isSessionRunning = false;
                            CloseLastSession();
                            connection.Open();
                        }
                        break;
                    case XmppConnectionState.SessionStarted:
                        {
                            if (!isSessionRunning)
                            {
                                StartNewSession();
                                isSessionRunning = true;
                            }
                        }
                        break;
                    default:
                        {
                            if (!activityDelayEnabled)
                            {
                                WaitActivity();
                                activityDelayEnabled = true;
                            }
                            else
                            {
                                activityDelayEnabled = false;
                                DisconnectByTimeout(state);
                            }
                        }
                        break;
                }

                Thread.Sleep(5000);
            }

            IsRunning = false;
        }

        private void SuspendConnectionIfNeeded()
        {
            if (suspendConnectionRequired)
            {
                connection.Close();

                Thread.Sleep((int)suspendConnectionTime.TotalMilliseconds);

                suspendConnectionRequired = false;
            }
        }

        private void CloseLastSession()
        {
            ISession lastSession = GetLastSession();
            if (lastSession != null)
                lastSession.Close();
        }

        private void StartNewSession()
        {
            ISession session = new Session();
            Sessions.Add(session);

            if (connection.OnSessionStarted != null)
            {
                connection.OnSessionStarted(session);
            }

            Logger.Write(String.Format("Started new session ID=[{0}] Date=[{1}]", session.ID, session.StartDate), LogMessageType.Info);
        }

        private ISession GetLastSession()
        {
            if (Sessions.Count > 0)
                return Sessions[Sessions.Count - 1];

            return null;
        }

        private void WaitActivity()
        {
            Thread.Sleep(WaitActivityTimeout);
        }

        private XmppConnectionState GetConnectionState()
        {
            return connection.State;
        }

        private void DisconnectByTimeout(XmppConnectionState state)
        {
            Logger.Write(String.Format("Disconnecting by timeout from state [{0}]", state), LogMessageType.Info);

            connection.Close();
        }

        #endregion

        #region Private Fields

        private IConnection connection;

        private bool terminate;
        private bool isSessionRunning;

        private Thread thread;
        private bool activityDelayEnabled;

        private TimeSpan suspendConnectionTime;
        private bool suspendConnectionRequired;

        #endregion
    }
}
